package com.example.viewtest;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.OverScroller;

public class ScaleImageView extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, Runnable {

    private static final float DIFAULT_IMAGE_WIDTH = Utils.db2px(200);
    private static final float BTG_SCALE_FACTOR = 2;

    private Bitmap bitmap;
    private Paint paint;
    private float imageHeight;
    private float imageWidth;
    private PointF originalOffset;
    private GestureDetectorCompat gestureDetectorCompat;
    private ScaleGestureDetector scaleGestureDetector;

    private float smallScale;
    private float bigScale;
    private boolean big;
    private float curScale;
    private ObjectAnimator scaleAnimatory;

    private PointF offset;
    private OverScroller scroller;

    public ScaleImageView(Context context) {
        this(context, null);
    }

    public ScaleImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScaleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bitmap = Utils.getBitmap(getResources(), R.mipmap.head, (int) DIFAULT_IMAGE_WIDTH);
        gestureDetectorCompat = new GestureDetectorCompat(context, this);
        scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
        gestureDetectorCompat.setOnDoubleTapListener(this);
        scroller = new OverScroller(context);
        imageWidth = bitmap.getWidth();
        imageHeight = bitmap.getHeight();
        offset = new PointF();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        originalOffset = new PointF((w - imageWidth) / 2, (h - imageHeight) / 2);
        if (imageWidth / imageHeight > w / h) {
            smallScale = w / imageWidth;
            bigScale = h / imageHeight * BTG_SCALE_FACTOR;
        } else {
            smallScale = h / imageHeight;
            bigScale = w / imageWidth * BTG_SCALE_FACTOR;
        }
        curScale = smallScale;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        revise(offset);
        float scale = (curScale - smallScale) / (bigScale - smallScale);
        canvas.translate(offset.x * scale, offset.y * scale);
        canvas.scale(curScale, curScale, getWidth() / 2, getHeight() / 2);
        canvas.translate(originalOffset.x, originalOffset.y);
        canvas.drawBitmap(bitmap, 0, 0, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean res1 = gestureDetectorCompat.onTouchEvent(event);
        boolean res2 = scaleGestureDetector.onTouchEvent(event);
        if (!(res1 || res2)) {
            return super.onTouchEvent(event);
        }
        return true;
    }


    @Override
    public boolean onDown(MotionEvent e) {
        //按下回调，返回true标识消耗次事件
        if (!scroller.isFinished()) {
            scroller.forceFinished(true);
        }
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        //按下后100ms回调
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        //抬起
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent down, MotionEvent e2, float distanceX, float distanceY) {
        //滑动
        if (big) {
            offset.offset(-distanceX, -distanceY);
            revise(offset);
            invalidate();
        }
        return false;
    }

    private void revise(PointF point) {
        float offsetW = (imageWidth * bigScale - getWidth()) / 2;
        float offsetH = (imageHeight * bigScale - getHeight()) / 2;
        point.x = Math.min(offsetW, point.x);
        point.x = Math.max(-offsetW, point.x);
        point.y = Math.min(offsetH, point.y);
        point.y = Math.max(-offsetH, point.y);
    }

    @Override
    public void onLongPress(MotionEvent e) {
        //长按
    }

    @Override
    public boolean onFling(MotionEvent down, MotionEvent e2, float velocityX, float velocityY) {
        //快速滑动
        if (big) {
            float offsetW = (imageWidth * bigScale - getWidth()) / 2;
            float offsetH = (imageHeight * bigScale - getHeight()) / 2;
            scroller.fling((int) offset.x, (int) offset.y, (int) velocityX, (int) velocityY,
                    (int) -offsetW, (int) offsetW, (int) -offsetH, (int) offsetH);
            postOnAnimation(this);
        }
        return false;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        //确认单击
        return false;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        //双击
        big = !big;
        if (big) {
            offset.set((getWidth() / 2 - e.getX()) * (bigScale - smallScale) / smallScale,
                    (getHeight() / 2 - e.getY()) * (bigScale - smallScale) / smallScale);
            getScaleAnimatory().start();
        } else {
            getScaleAnimatory().reverse();
        }
        return false;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        //整个双击的事件流
        return false;
    }

    public float getCurScale() {
        return curScale;
    }

    public void setCurScale(float curScale) {
        this.curScale = curScale;
        invalidate();
    }

    private ObjectAnimator getScaleAnimatory() {
        if (scaleAnimatory == null) {
            scaleAnimatory = ObjectAnimator.ofFloat(this, "curScale", smallScale, bigScale);
            scaleAnimatory.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    if (!big) {
                        offset.set(0, 0);
                    }
                }
            });
        }
        return scaleAnimatory;
    }

    @Override
    public void run() {
        if (scroller.computeScrollOffset()) {
            offset.set(scroller.getCurrX(), scroller.getCurrY());
            invalidate();
            postOnAnimation(this);
        }
    }

    private class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
        float lastScale;

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            curScale = lastScale * detector.getScaleFactor();
            curScale = Math.min(curScale, bigScale);
            curScale = Math.max(curScale, smallScale);
            invalidate();
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            lastScale = curScale;
            big = curScale >= bigScale;
            return curScale <= bigScale && curScale >= smallScale;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

        }
    }
}
